/*
OPCClientToolKit
Copyright (C) 2005 Mark C. Beharrell

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA  02111-1307, USA.
*/

#include "OPCServer.h"













COPCServer::COPCServer(ATL::CComPtr<IOPCServer> &opcServerInterface){
	iOpcServer = opcServerInterface;

	HRESULT res = opcServerInterface->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void**)&iOpcNamespace);
	if (FAILED(res)){
		throw OPCException("Failed to obtain IID_IOPCBrowseServerAddressSpace interface",res);
	}

	res = opcServerInterface->QueryInterface(IID_IOPCItemProperties, (void**)&iOpcProperties);
	if (FAILED(res)){
		throw OPCException("Failed to obtain IID_IOPCItemProperties interface",res);
	}
}



COPCServer::~COPCServer()
{

}



COPCGroup *COPCServer::makeGroup(const CString & groupName, bool active, unsigned long reqUpdateRate_ms, unsigned long &revisedUpdateRate_ms, float deadBand){
	return new COPCGroup(groupName, active, reqUpdateRate_ms, revisedUpdateRate_ms, deadBand, *this);
}



void COPCServer::getBranches(CAtlArray<CString> & opcItemNames){
	if (!iOpcNamespace) return;

	OPCNAMESPACETYPE nameSpaceType;
	HRESULT result = iOpcNamespace->QueryOrganization(&nameSpaceType);
	
	USES_CONVERSION;
	int v = 0;
	WCHAR emptyString[] = {0};
	result = iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_TO,emptyString);

	ATL::CComPtr<IEnumString> iEnum;
	//result = iOpcNamespace->BrowseOPCItemIDs(OPC_FLAT,emptyString,VT_EMPTY,0,(&iEnum));
	result = iOpcNamespace->BrowseOPCItemIDs(OPC_BRANCH,emptyString,VT_EMPTY,0,(&iEnum));
	if (FAILED(result)){
		return;
	}	 
    WCHAR * str;
    ULONG strSize;
	while((result = iEnum->Next(1, &str, &strSize)) == S_OK)
	{
		WCHAR * fullName;
		result = iOpcNamespace->GetItemID(str, &fullName);
		if (SUCCEEDED(result)){
			USES_CONVERSION;
			char * cStr = OLE2T(fullName);
			//char * cStr = OLE2T(str);
			//printf("Adding %s\n", cStr);
			opcItemNames.Add(cStr);
			COPCClient::comFree(fullName);
		}
		COPCClient::comFree(str);
	}
}

void COPCServer::getLeafs(CAtlArray<CString> & opcItemNames){
	if (!iOpcNamespace) return;

	OPCNAMESPACETYPE nameSpaceType;
	HRESULT result = iOpcNamespace->QueryOrganization(&nameSpaceType);
	
	USES_CONVERSION;
	int v = 0;
	WCHAR emptyString[] = {0};
	result = iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_TO,emptyString);

	ATL::CComPtr<IEnumString> iEnum;
	//result = iOpcNamespace->BrowseOPCItemIDs(OPC_FLAT,emptyString,VT_EMPTY,0,(&iEnum));
	result = iOpcNamespace->BrowseOPCItemIDs(OPC_LEAF,emptyString,VT_EMPTY,0,(&iEnum));
	if (FAILED(result)){
		return;
	}	 
    WCHAR * str;
    ULONG strSize;
	while((result = iEnum->Next(1, &str, &strSize)) == S_OK)
	{
		WCHAR * fullName;
		result = iOpcNamespace->GetItemID(str, &fullName);
		if (SUCCEEDED(result)){
			USES_CONVERSION;
			char * cStr = OLE2T(fullName);
			//char * cStr = OLE2T(str);
			//printf("Adding %s\n", cStr);
			opcItemNames.Add(cStr);
			COPCClient::comFree(fullName);
		}
		COPCClient::comFree(str);
	}
}




void COPCServer::getItemNames(CAtlArray<CString> & opcItemNames){
	if (!iOpcNamespace) return;

	OPCNAMESPACETYPE nameSpaceType;
	HRESULT result = iOpcNamespace->QueryOrganization(&nameSpaceType);
	
	USES_CONVERSION;
	int v = 0;
	WCHAR emptyString[] = {0};
	result = iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_TO,emptyString);

	ATL::CComPtr<IEnumString> iEnum;
	result = iOpcNamespace->BrowseOPCItemIDs(OPC_FLAT,emptyString,VT_EMPTY,0,(&iEnum));
	//result = iOpcNamespace->BrowseOPCItemIDs(OPC_BRANCH,emptyString,VT_EMPTY,0,(&iEnum));
	if (FAILED(result)){
		return;
	}	 
    WCHAR * str;
    ULONG strSize;
	while((result = iEnum->Next(1, &str, &strSize)) == S_OK)
	{
		WCHAR * fullName;
		result = iOpcNamespace->GetItemID(str, &fullName);
		if (SUCCEEDED(result)){
			USES_CONVERSION;
			char * cStr = OLE2T(fullName);
			//char * cStr = OLE2T(str);
			//printf("Adding %s\n", cStr);
			opcItemNames.Add(cStr);
			COPCClient::comFree(fullName);
		}
		COPCClient::comFree(str);
	}
}


void COPCServer::resolveOneLevel(WCHAR* branchName, CAtlArray<CAtlString>* tmpOpcItemNames) 
{ 
   OPCNAMESPACETYPE nameSpaceType; 
   USES_CONVERSION; 
   
   WCHAR emptyString[] = {0}; 
   ATL::CComPtr<IEnumString> iEnum; 
   ATL::CComPtr<IEnumString> iEnumLeaf; 
   ATL::CComPtr<IEnumString> iEnumBranch; 
   WCHAR* str; 
   ULONG strSize; 

   HRESULT result = iOpcNamespace->QueryOrganization(&nameSpaceType); 

   if (nameSpaceType == OPC_NS_FLAT) 
   { 
      result = iOpcNamespace->BrowseOPCItemIDs(OPC_FLAT, emptyString, VT_EMPTY, 0, (&iEnum)); 
      if (FAILED(result)){ 
         return; 
      } 
      while((result = iEnum->Next(1, &str, &strSize)) == S_OK) 
      { 
         WCHAR* fullName; 
         result = iOpcNamespace->GetItemID(str, &fullName); 
         if (SUCCEEDED(result)){ 
            USES_CONVERSION; 
            char* cStr = OLE2T(fullName); 
            tmpOpcItemNames->Add(cStr); 
            COPCClient::comFree(fullName); 
         } 
         COPCClient::comFree(str); 
      } 
   } 
   else 
   if (nameSpaceType == OPC_NS_HIERARCHIAL) 
   { 
      // Browse LEAF 
      result = iOpcNamespace->BrowseOPCItemIDs(OPC_LEAF, emptyString, VT_EMPTY, 0, &iEnumLeaf); 
      if (FAILED(result)){ 
         return; 
      } 
      while((result = iEnumLeaf->Next(1, &str, &strSize)) == S_OK) 
      { 
         WCHAR* fullName; 
         result = iOpcNamespace->GetItemID(str, &fullName); 
         if (SUCCEEDED(result)) 
         { 
            USES_CONVERSION; 
            char* cStr = OLE2T(fullName); 
            tmpOpcItemNames->Add(cStr); 
            COPCClient::comFree(fullName); 
         } 
         COPCClient::comFree(str); 
      } 

	    
      
	  // Browse BRANCH 
      result = iOpcNamespace->BrowseOPCItemIDs(OPC_BRANCH, branchName, VT_EMPTY, 0, (&iEnumBranch)); 

      if (FAILED(result))   { 
         return; 
      } 
      while((result = iEnumBranch->Next(1, &str, &strSize)) == S_OK) 
      { 
         iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_DOWN, str); 		  
         resolveOneLevel(str, tmpOpcItemNames);//recursive Call to resolveOneLevel
		 iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_UP, str); 

      }
   }
} 


//iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_TO, emptyString); 
//result = iOpcNamespace->BrowseOPCItemIDs(OPC_LEAF, emptyString, VT_EMPTY, 0, &iEnumLeaf); 



//void COPCServer::getItemNames(char** &opcItemNames, int* count){ 
void COPCServer::getItemNames(char** &opcItemNames, int &count){ 
   
   if (!iOpcNamespace) return; 
    
   
   CAtlArray<CAtlString> tmpOpcItemNames; 
   WCHAR emptyString[] = {0}; 

   OPCNAMESPACETYPE nameSpaceType;
   HRESULT result = iOpcNamespace->QueryOrganization(&nameSpaceType);
   //printf("namespaceType: %d",nameSpaceType);
   

   iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_TO, emptyString); 
   resolveOneLevel(emptyString, &tmpOpcItemNames); 

   //(*count) = tmpOpcItemNames.GetCount(); 
   count = tmpOpcItemNames.GetCount();
   //printf("\nITEMS DETECTADOS: %d",count);	
   
   //opcItemNames = new char* [*count]; 
   opcItemNames = new char* [count];

   //for (unsigned i = 0; i < *count; i++) 
   for (unsigned i = 0; i < count; i++) 
   { 
      opcItemNames[i]=new char[tmpOpcItemNames[i].GetLength()+1]; 
      strcpy (opcItemNames[i], tmpOpcItemNames[i]); 
   } 
}

/*void COPCServer::getItemNames(char** &opcItemNames, int &count){ 
   
   if (!iOpcNamespace) return; 
    
   OPCNAMESPACETYPE nameSpaceType; 
   CAtlArray<CAtlString> tmpOpcItemNames; 
   WCHAR emptyString[] = {0};

   
   HRESULT result = iOpcNamespace->QueryOrganization(&nameSpaceType);


   CAtlArray<CString> opcItemNamesCString;	
   printf("namespaceType: %d",nameSpaceType);
	

   if(nameSpaceType==OPC_BRANCH){
	   

	   getItemNames(opcItemNamesCString);	   
	   count = opcItemNamesCString.GetCount();
	   printf("\ncount = %d",count);

	   opcItemNames = new char* [count];
	   for (unsigned i = 0; i < count; i++) 
	   { 
		  opcItemNames[i]=new char[opcItemNamesCString[i].GetLength()+1]; 
		  strcpy (opcItemNames[i], opcItemNamesCString[i]); 
	   } 	   
   }
   else{
   	
	   iOpcNamespace->ChangeBrowsePosition(OPC_BROWSE_TO, emptyString); 
	   resolveOneLevel(emptyString, &tmpOpcItemNames); 

	   //(*count) = tmpOpcItemNames.GetCount(); 
	   count = tmpOpcItemNames.GetCount(); 
	   //opcItemNames = new char* [*count]; 
	   opcItemNames = new char* [count];

	   //for (unsigned i = 0; i < *count; i++) 
	   for (unsigned i = 0; i < count; i++) 
	   { 
		  opcItemNames[i]=new char[tmpOpcItemNames[i].GetLength()+1]; 
		  strcpy (opcItemNames[i], tmpOpcItemNames[i]); 
	   } 
   }
}*/

void COPCServer::getStatus(ServerStatus &status){
	OPCSERVERSTATUS *serverStatus;
	HRESULT result = iOpcServer->GetStatus(&serverStatus);
	if (FAILED(result)){
		throw OPCException("Failed to get status");
	}

	status.ftStartTime = serverStatus->ftStartTime;
    status.ftCurrentTime = serverStatus->ftCurrentTime;
    status.ftLastUpdateTime = serverStatus->ftLastUpdateTime;
    status.dwServerState = serverStatus->dwServerState;
    status.dwGroupCount = serverStatus->dwGroupCount;
    status.dwBandWidth = serverStatus->dwBandWidth;
    status.wMajorVersion = serverStatus->wMajorVersion;
    status.wMinorVersion = serverStatus->wMinorVersion;
    status.wBuildNumber = serverStatus->wBuildNumber;
	if (serverStatus->szVendorInfo != NULL){
		USES_CONVERSION;
		status.vendorInfo = OLE2T(serverStatus->szVendorInfo);
		COPCClient::comFree(serverStatus->szVendorInfo);
	}
	COPCClient::comFree(serverStatus);
}